package com.zeratul.gateway.filter;

import cn.hutool.core.map.MapUtil;
import com.zeratul.common.core.constant.MdcConstants;
import com.zeratul.common.json.utils.JsonUtils;
import com.zeratul.gateway.application.prop.ZeratulGatewayProperties;
import com.zeratul.gateway.utils.WebFluxUtils;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Map;

/**
 * 全局日志过滤器
 * 用于打印请求执行参数与响应时间等等
 */
@Slf4j
@Component
public class GlobalLogFilter implements GlobalFilter, Ordered {

    @Autowired
    private ZeratulGatewayProperties zeratulGatewayProperties;

    private static final String START_TIME = "startTime";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        if (!zeratulGatewayProperties.getRequestLog()) {
            return chain.filter(exchange);
        }
        ServerHttpRequest request = exchange.getRequest();
        String path = WebFluxUtils.getOriginalRequestUrl(exchange);
        String url = request.getMethod().name() + " " + path;
        //获取请求头
        String traceId = request.getHeaders().getFirst(MdcConstants.TRACE_ID);

        //设置全局TraceId
        MDC.put(MdcConstants.TRACE_ID, traceId);
        MDC.put(MdcConstants.REQUEST_URL_KEY, url);

        // 打印请求参数
        if (WebFluxUtils.isJsonRequest(exchange)) {
            String jsonParam = WebFluxUtils.resolveBodyFromCacheRequest(exchange);
            log.debug("【Gateway】开始请求 => 参数类型[json], 参数:[{}]", jsonParam);
        } else {
            MultiValueMap<String, String> parameterMap = request.getQueryParams();
            if (MapUtil.isNotEmpty(parameterMap)) {
                String parameters = JsonUtils.toJsonString(parameterMap);
                log.debug("【Gateway】开始请求 => 参数类型[param], 参数:[{}]", parameters);
            } else {
                log.debug("【Gateway】开始请求 => 无参数");
            }
        }

        //设置起始时间
        exchange.getAttributes().put(START_TIME, System.currentTimeMillis());
        try {
            //获取MDC对象
            Map<String, String> copyOfContextMap = MDC.getCopyOfContextMap();
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                //设置MDC，避免线程切换丢失MDC数据
                MDC.setContextMap(copyOfContextMap);
                Long startTime = exchange.getAttribute(START_TIME);
                if (startTime != null) {
                    long executeTime = (System.currentTimeMillis() - startTime);
                    log.debug("【Gateway】结束请求 => 耗时:[{}]毫秒", executeTime);
                }
                //回收MDC资源
                MDC.clear();
            }));
        } finally {
            //MDC资源回收
            MDC.clear();
        }
    }

    @Override
    public int getOrder() {
        // 日志处理器在负载均衡器之后执行 负载均衡器会导致线程切换 无法获取上下文内容
        // 如需在日志内操作线程上下文 例如获取登录用户数据等 可以打开下方注释代码
        // return ReactiveLoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER - 1;
        return Ordered.LOWEST_PRECEDENCE;
    }

}
